//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.nioneo.xa
{


	using Node = org.neo4j.graphdb.Node;
	using Relationship = org.neo4j.graphdb.Relationship;
	using RelationshipType = org.neo4j.graphdb.RelationshipType;
	using LockReleaser = org.neo4j.kernel.impl.core.LockReleaser;
	using PropertyIndex = org.neo4j.kernel.impl.core.PropertyIndex;
	using NeoStore = org.neo4j.kernel.impl.nioneo.store.NeoStore;
	using PropertyStore = org.neo4j.kernel.impl.nioneo.store.PropertyStore;
	using Store = org.neo4j.kernel.impl.nioneo.store.Store;
	using IdGenerationFailedException = org.neo4j.kernel.impl.persistence.IdGenerationFailedException;
	using LockManager = org.neo4j.kernel.impl.transaction.LockManager;
	using XaCommand = org.neo4j.kernel.impl.transaction.xaframework.XaCommand;
	using XaCommandFactory = org.neo4j.kernel.impl.transaction.xaframework.XaCommandFactory;
	using XaConnection = org.neo4j.kernel.impl.transaction.xaframework.XaConnection;
	using XaContainer = org.neo4j.kernel.impl.transaction.xaframework.XaContainer;
	using XaDataSource = org.neo4j.kernel.impl.transaction.xaframework.XaDataSource;
	using XaTransaction = org.neo4j.kernel.impl.transaction.xaframework.XaTransaction;
	using XaTransactionFactory = org.neo4j.kernel.impl.transaction.xaframework.XaTransactionFactory;
	using ArrayMap = org.neo4j.kernel.impl.util.ArrayMap;

///
/// <summary> * A <CODE>NeoStoreXaDataSource</CODE> is a factory for
/// * <seealso cref="NeoStoreXaConnection NeoStoreXaConnections"/>.
/// * <p>
/// * The <seealso cref="NioNeoDbPersistenceSource"/> will create a <CODE>NeoStoreXaDataSoruce</CODE>
/// * and then Neo4j kernel will use it to create <seealso cref="XaConnection XaConnections"/> and
/// * <seealso cref="XaResource XaResources"/> when running transactions and performing
/// * operations on the node space. </summary>
/// 
	public class NeoStoreXaDataSource : XaDataSource
	{
		private static Logger logger = Logger.getLogger(typeof(NeoStoreXaDataSource).Name);

		private readonly NeoStore neoStore;
		private readonly XaContainer xaContainer;
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: private final ArrayMap<Class<?>,Store> idGenerators;
		private readonly ArrayMap<Class<?>, Store> idGenerators;

		private readonly LockManager lockManager;
		private readonly LockReleaser lockReleaser;
		private readonly string storeDir;
		private bool readOnly = false;

		private bool logApplied = false;

///    
///     <summary> * Creates a <CODE>NeoStoreXaDataSource</CODE> using configuration from
///     * <CODE>params</CODE>. First the map is checked for the parameter 
///     * <CODE>config</CODE>.
///     * If that parameter exists a config file with that value is loaded (via
///     * <seealso cref="Properties#load"/>). Any parameter that exist in the config file
///     * and in the map passed into this constructor will take the value from the
///     * map.
///     * <p>
///     * If <CODE>config</CODE> parameter is set but file doesn't exist an
///     * <CODE>IOException</CODE> is thrown. If any problem is found with that
///     * configuration file or Neo4j store can't be loaded an <CODE>IOException is
///     * thrown</CODE>.
///     *  </summary>
///     * <param name="params">
///     *            A map containing configuration parameters and/or configuration
///     *            file. </param>
///     * <exception cref="IOException">
///     *             If unable to create data source </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public NeoStoreXaDataSource(Map<Object,Object> config) throws IOException, InstantiationException
		public NeoStoreXaDataSource(Map<object, object> config) : base(config)
		{
			if (config.get("read_only") != null)
			{
				readOnly = (bool) config.get("read_only");
			}
			this.lockManager = (LockManager) config.get(typeof(LockManager));
			this.lockReleaser = (LockReleaser) config.get(typeof(LockReleaser));
		// String configFileName = (String) params.get( "config" );
			storeDir = (string) config.get("store_dir");
//        Properties config = new Properties();
//        if ( configFileName != null )
//        {
//            FileInputStream inputStream = new FileInputStream( configFileName );
//            try
//            {
//                config.load( inputStream );
//            }
//            finally
//            {
//                inputStream.close();
//            }
//        }
//        IEnumerator  itr = params.entrySet().iterator();
//        while ( itr.hasNext() )
//        {
//            Map.Entry entry = (Map.Entry) itr.next();
//            if ( entry.getKey() instanceof String
//                && entry.getValue() instanceof String )
//            {
//                config.setProperty( (String) entry.getKey(), (String) entry
//                    .getValue() );
//            }
//        }
			string store = (string) config.get("neo_store");
			config.put("rebuild_idgenerators_fast", "true");
			File file = new File(store);
			string create = "" + config.get("create");
			if (!readOnly && !file.exists() && "true".Equals(create))
			{
				autoCreatePath(store);
				NeoStore.createStore(store);
			}

			neoStore = new NeoStore(config);
			xaContainer = XaContainer.create((string) config.get("logical_log"), new CommandFactory(neoStore), new TransactionFactory(), config);

			if (!readOnly)
			{
				xaContainer.openLogicalLog();
			}
			if (!xaContainer.getResourceManager().hasRecoveredTransactions())
			{
				neoStore.makeStoreOk();
			}
			else
			{
				logger.fine("Waiting for TM to take care of recovered " + "transactions.");
			}
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: idGenerators = new ArrayMap<Class<?>,Store>(5, false, false);
			idGenerators = new ArrayMap<Class<?>, Store>(5, false, false);
			this.idGenerators.put(typeof(Node), neoStore.getNodeStore());
			this.idGenerators.put(typeof(Relationship), neoStore.getRelationshipStore());
			this.idGenerators.put(typeof(RelationshipType), neoStore.getRelationshipTypeStore());
			this.idGenerators.put(typeof(PropertyStore), neoStore.getPropertyStore());
			this.idGenerators.put(typeof(PropertyIndex), neoStore.getPropertyStore().getIndexStore());
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: private void autoCreatePath(String store) throws IOException
		private void autoCreatePath(string store)
		{
			string fileSeparator = System.getProperty("file.separator");
			int index = store.LastIndexOf(fileSeparator);
			string dirs = store.Substring(0, index);
			File directories = new File(dirs);
			if (!directories.exists())
			{
				if (!directories.mkdirs())
				{
					throw new IOException("Unable to create directory path[" + dirs + "] for Neo4j store.");
				}
			}
		}

///    
///     <summary> * Creates a data source with minimum (no memory mapped) configuration.
///     *  </summary>
///     * <param name="neoStoreFileName">
///     *            The file name of the store </param>
///     * <param name="logicalLogPath">
///     *            The file name of the logical log </param>
///     * <exception cref="IOException">
///     *             If unable to open store </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public NeoStoreXaDataSource(String neoStoreFileName, String logicalLogPath, LockManager lockManager, LockReleaser lockReleaser) throws IOException, InstantiationException
		public NeoStoreXaDataSource(string neoStoreFileName, string logicalLogPath, LockManager lockManager, LockReleaser lockReleaser) : base(null)
		{
			this.lockManager = lockManager;
			this.lockReleaser = lockReleaser;
			storeDir = logicalLogPath;
			neoStore = new NeoStore(neoStoreFileName);
			xaContainer = XaContainer.create(logicalLogPath, new CommandFactory(neoStore), new TransactionFactory(), null);

			xaContainer.openLogicalLog();
			if (!xaContainer.getResourceManager().hasRecoveredTransactions())
			{
				neoStore.makeStoreOk();
			}
			else
			{
				logger.info("Waiting for TM to take care of recovered " + "transactions.");
			}
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: idGenerators = new ArrayMap<Class<?>,Store>(5, false, false);
			idGenerators = new ArrayMap<Class<?>, Store>(5, false, false);
			this.idGenerators.put(typeof(Node), neoStore.getNodeStore());
			this.idGenerators.put(typeof(Relationship), neoStore.getRelationshipStore());
			this.idGenerators.put(typeof(RelationshipType), neoStore.getRelationshipTypeStore());
		// get TestXa unit test to run
			this.idGenerators.put(typeof(PropertyStore), neoStore.getPropertyStore());
			this.idGenerators.put(typeof(PropertyIndex), neoStore.getPropertyStore().getIndexStore());
		}

		internal virtual NeoStore getNeoStore()
		{
			return neoStore;
		}

		public override void close()
		{
			xaContainer.Close();
			if (logApplied)
			{
				neoStore.rebuildIdGenerators();
				logApplied = false;
			}
			neoStore.Close();
			logger.fine("NeoStore closed");
		}

		public override XaConnection getXaConnection()
		{
			return new NeoStoreXaConnection(neoStore, xaContainer.getResourceManager(), getBranchId());
		}

		private class CommandFactory : XaCommandFactory
		{
			private NeoStore neoStore = null;

			internal CommandFactory(NeoStore neoStore)
			{
				this.neoStore = neoStore;
			}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public XaCommand readCommand(ReadableByteChannel byteChannel, ByteBuffer buffer) throws IOException
			public override XaCommand readCommand(ReadableByteChannel byteChannel, ByteBuffer buffer)
			{
				Command command = Command.readCommand(neoStore, byteChannel, buffer);
				if (command != null)
				{
					command.setRecovered();
				}
				return command;
			}
		}

		private class TransactionFactory : XaTransactionFactory
		{
			internal TransactionFactory()
			{
			}

			public override XaTransaction create(int identifier)
			{
				return new NeoTransaction(identifier, getLogicalLog(), neoStore, lockReleaser, lockManager);
			}

			public override void recoveryComplete()
			{
				logger.fine("Recovery complete, " + "all transactions have been resolved");
				logger.fine("Rebuilding id generators as needed. " + "This can take a while for large stores...");
				neoStore.flushAll();
				neoStore.makeStoreOk();
				logger.fine("Rebuild of id generators complete.");
			}

			public override long getCurrentVersion()
			{
				if (getLogicalLog().scanIsComplete())
				{
					return neoStore.getVersion();
				}
				neoStore.setRecoveredStatus(true);
				try
				{
					return neoStore.getVersion();
				}
				finally
				{
					neoStore.setRecoveredStatus(false);
				}
			}

			public override long getAndSetNewVersion()
			{
				return neoStore.incrementVersion();
			}

			public override void flushAll()
			{
				neoStore.flushAll();
			}
		}

		public virtual int nextId<T1>(System.Type<T1> clazz)
		{
			Store store = idGenerators.get(clazz);

			if (store == null)
			{
				throw new IdGenerationFailedException("No IdGenerator for: " + clazz);
			}
			return store.nextId();
		}

		public virtual long getHighestPossibleIdInUse<T1>(System.Type<T1> clazz)
		{
			Store store = idGenerators.get(clazz);
			if (store == null)
			{
				throw new IdGenerationFailedException("No IdGenerator for: " + clazz);
			}
			return store.getHighestPossibleIdInUse();
		}

		public virtual long getNumberOfIdsInUse<T1>(System.Type<T1> clazz)
		{
			Store store = idGenerators.get(clazz);
			if (store == null)
			{
				throw new IdGenerationFailedException("No IdGenerator for: " + clazz);
			}
			return store.getNumberOfIdsInUse();
		}

		public virtual string getStoreDir()
		{
			return storeDir;
		}

		public override void keepLogicalLogs(bool keep)
		{
			xaContainer.getLogicalLog().setKeepLogs(keep);
		}

		public override long getCreationTime()
		{
			return neoStore.getCreationTime();
		}

		public override long getRandomIdentifier()
		{
			return neoStore.getRandomNumber();
		}

		public override long getCurrentLogVersion()
		{
			return neoStore.getVersion();
		}

		public virtual long incrementAndGetLogVersion()
		{
			return neoStore.incrementVersion();
		}

		public virtual void setCurrentLogVersion(long version)
		{
			neoStore.setVersion(version);
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void applyLog(ReadableByteChannel byteChannel) throws IOException
		public override void applyLog(ReadableByteChannel byteChannel)
		{
			logApplied = true;
			xaContainer.getLogicalLog().applyLog(byteChannel);
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void rotateLogicalLog() throws IOException
		public override void rotateLogicalLog()
		{
		// flush done inside rotate
			xaContainer.getLogicalLog().rotate();
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public ReadableByteChannel getLogicalLog(long version) throws IOException
		public override ReadableByteChannel getLogicalLog(long version)
		{
			return xaContainer.getLogicalLog().getLogicalLog(version);
		}

		public virtual long getLogicalLogLength(long version)
		{
			return xaContainer.getLogicalLog().getLogicalLogLength(version);
		}

		public override bool hasLogicalLog(long version)
		{
			return xaContainer.getLogicalLog().hasLogicalLog(version);
		}

		public override bool deleteLogicalLog(long version)
		{
			return xaContainer.getLogicalLog().deleteLogicalLog(version);
		}

		public override void setAutoRotate(bool rotate)
		{
			xaContainer.getLogicalLog().setAutoRotateLogs(rotate);
		}

		public override void setLogicalLogTargetSize(long size)
		{
			xaContainer.getLogicalLog().setLogicalLogTargetSize(size);
		}

		public override void makeBackupSlave()
		{
			xaContainer.getLogicalLog().makeBackupSlave();
		}

		internal virtual NeoReadTransaction getReadOnlyTransaction()
		{
			return new NeoReadTransaction(neoStore);
		}

		public virtual bool isReadOnly()
		{
			return readOnly;
		}

		public virtual string getFileName(long version)
		{
			return xaContainer.getLogicalLog().getFileName(version);
		}
	}
}